package com.avo.ramcache.config;

import com.avo.ramcache.anno.core.injectCache;
import com.avo.ramcache.anno.core.type.CacheUnit;
import com.avo.ramcache.manager.CacheServiceManager;
import com.avo.ramcache.service.ICacheService;
import com.avo.ramcache.util.ReflectionUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

@SuppressWarnings({"rawtypes", "unchecked"})
@Component
public class InjectCacheServiceProcessor implements Ordered, InstantiationAwareBeanPostProcessor {

    @Resource
    private CacheServiceManager cacheServiceManager;

    @Override
    public boolean postProcessAfterInstantiation(Object bean, @NonNull String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), field -> {
            if (!field.isAnnotationPresent(injectCache.class)) {
                return;
            }
            Class clz = getClass(field);
            CachedEntityConfig entityConfig = cacheServiceManager.getEntityConfig(clz);
            if (entityConfig == null) {
                throw new RuntimeException("bean " + beanName + " 的注入属性 " + field.getName() + " 声明错误");
            }
            CacheUnit cacheUnit = entityConfig.getCached().unit();
            ICacheService cacheService = cacheUnit.getCacheService(cacheServiceManager, clz);
            inject(bean, field, cacheService);
        });
        return true;
    }

    private Class getClass(Field field) {
        Type type = field.getGenericType();
        Type[] types = ((ParameterizedType) type).getActualTypeArguments();
        return (Class) types[1];
    }

    private void inject(Object bean, Field field, Object value) {
        try {
            ReflectionUtil.makeAccessible(field);
            field.set(bean, value);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("属性 " + field + " 注入失败");
        }
    }

    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    }

}
